home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / v cisle / sadanastroju / lightning-0.8-tb-win.xpi / chrome / calendar.jar / content / calendar / calendar-recurrence-dialog.js < prev    next >
Text File  |  2007-11-05  |  15KB  |  395 lines

  1. /* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is Oracle Corporation code.
  16.  *
  17.  * The Initial Developer of the Original Code is Oracle Corporation
  18.  * Portions created by the Initial Developer are Copyright (C) 2005
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *   Stuart Parmenter <stuart.parmenter@oracle.com>
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. /* dialog stuff */
  39. function onLoad()
  40. {
  41.     var args = window.arguments[0];
  42.  
  43.     window.onAcceptCallback = args.onOk;
  44.     window.calendarEvent = args.calendarEvent;
  45.     window.originalRecurrenceInfo = args.recurrenceInfo;
  46.     window.startDate = args.startDate;
  47.  
  48.     loadDialog();
  49.  
  50.     updateDeck();
  51.  
  52.     updateDuration();
  53.  
  54.     updateAccept();
  55.  
  56.     opener.setCursor("auto");
  57.  
  58.     self.focus();
  59. }
  60.  
  61. function onAccept()
  62. {
  63.     var event = window.calendarEvent;
  64.  
  65.     var recurrenceInfo = saveDialog();
  66.  
  67.     window.onAcceptCallback(recurrenceInfo);
  68.  
  69.     return true;
  70. }
  71.  
  72. function onCancel()
  73. {
  74.  
  75. }
  76.  
  77. function loadDialog()
  78. {
  79.     // Start with setting some labels, that depend on the (start)date of the item
  80.     // Those labels are for the monthly recurrence deck.
  81.     var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  82.                         .getService(Components.interfaces.nsIStringBundleService);
  83.     var props = sbs.createBundle("chrome://calendar/locale/dateFormat.properties");
  84.  
  85.     // Set label to '15th day of the month'
  86.     var nthstr = props.GetStringFromName("ordinal.suffix."+window.startDate.day);
  87.     var str = props.formatStringFromName("recurNthDay", [window.startDate.day, nthstr], 2);
  88.     document.getElementById("monthly-nth-day").label = str;
  89.  
  90.     // Set label to 'second week of the month'
  91.     // Note that the date here needs to be 0 based to work properly
  92.     var monthWeekNum = Math.floor((window.startDate.day - 1) / 7) + 1;
  93.  
  94.     // In order to remain somewhat sane for l10n, turns a number into a word
  95.     var numWordMap = ["", "first", "second", "third", "fourth", "fifth"];
  96.     var numDayMap = ["sunday", "monday", "tuesday", "wednesday",
  97.                      "thursday", "friday", "saturday"];
  98.  
  99.     str = calGetString("dateFormat", 
  100.                        "recur." + numWordMap[monthWeekNum] + "." + 
  101.                        numDayMap[window.startDate.weekday]);
  102.     document.getElementById("monthly-nth-week").label = str;
  103.  
  104.     // Set two values needed to create the real rrule later
  105.     document.getElementById("monthly-nth-week").day = window.startDate.weekday;
  106.     document.getElementById("monthly-nth-week").week = monthWeekNum;
  107.  
  108.     // If this is the last friday of the month, set label to 'last friday of the month'
  109.     // (Or any other day, ofcourse.) Otherwise, hide last option
  110.     var monthLength = window.startDate.endOfMonth.day;
  111.     var isLastWeek = (monthLength - window.startDate.day) < 7;
  112.     document.getElementById("monthly-last-week").hidden = !isLastWeek;
  113.     var isLastDay = (monthLength == window.startDate.day);
  114.     document.getElementById("monthly-last-day").hidden = !isLastDay;
  115.     if (isLastWeek) {
  116.         str = calGetString("dateFormat", "recur.last." + numDayMap[window.startDate.weekday]);
  117.         document.getElementById("monthly-last-week").label = str;
  118.     }
  119.  
  120.     document.getElementById("monthly-last-week").day = window.startDate.weekday;
  121.  
  122.     if (!window.originalRecurrenceInfo)
  123.         return;
  124.  
  125.     /* split out rules and exceptions */
  126.     var rrules = splitRecurrenceRules(window.originalRecurrenceInfo);
  127.     var rules = rrules[0];
  128.     var exceptions = rrules[1];
  129.  
  130.     /* deal with the rules */
  131.     if (rules.length > 0) {
  132.         // we only handle 1 rule currently
  133.         var rule = rules[0];
  134.         if (rule instanceof Components.interfaces.calIRecurrenceRule) {
  135.  
  136.             switch(rule.type) {
  137.             case "DAILY":
  138.                 document.getElementById("period-list").selectedIndex = 0;
  139.  
  140.                 setElementValue("daily-days", rule.interval);
  141.                 break;
  142.             case "WEEKLY":
  143.                 document.getElementById("period-list").selectedIndex = 1;
  144.                 setElementValue("weekly-weeks", rule.interval);
  145.  
  146.                 const byDayTable = { 1 : "sun", 2 : "mon", 3 : "tue", 4 : "wed",
  147.                                      5 : "thu", 6 : "fri", 7: "sat" };
  148.  
  149.                 for each (var i in rule.getComponent("BYDAY", {})) {
  150.                     setElementValue("weekly-" + byDayTable[i], "true", "checked");
  151.                 }
  152.                 break;
  153.             case "MONTHLY":
  154.                 document.getElementById("period-list").selectedIndex = 2;
  155.                 // XXX This code ignores a lot of monthly recurrence rules that
  156.                 // can come in from external sources. There just is no UI to
  157.                 // show them
  158.                 setElementValue("monthly-months", rule.interval);
  159.                 var days = rule.getComponent("BYMONTHDAY", {});
  160.                 if (days.length > 0 && days[0]) {
  161.                     if (days[0] == -1) {
  162.                         calRadioGroupSelectItem("monthly-type", "monthly-last-day");
  163.                     } else {
  164.                         calRadioGroupSelectItem("monthly-type", "monthly-nth-day");
  165.                     }
  166.                 }
  167.                 days = rule.getComponent("BYDAY", {}) ;
  168.                 if (days.length > 0 && days[0] > 0) {
  169.                     calRadioGroupSelectItem("monthly-type", "monthly-nth-week");
  170.                 }
  171.                 if (days.length > 0 && days[0] < 0) {
  172.                     calRadioGroupSelectItem("monthly-type", "monthly-last-week");
  173.                 }
  174.                 break;
  175.             case "YEARLY":
  176.                 document.getElementById("period-list").selectedIndex = 3;
  177.                 break;
  178.             default:
  179.                 dump("unable to handle your rule type!\n");
  180.                 break;
  181.             }
  182.  
  183.             /* load up the duration of the event radiogroup */
  184.             if (rule.isByCount) {
  185.                 if (rule.count == -1) {
  186.                     setElementValue("recurrence-duration", "forever");
  187.                 } else {
  188.                     setElementValue("recurrence-duration", "ntimes");
  189.                     setElementValue("repeat-ntimes-count", rule.count );
  190.                 }
  191.             } else {
  192.                 var endDate = rule.endDate;
  193.                 if (!endDate) {
  194.                     setElementValue("recurrence-duration", "forever");
  195.                 } else {
  196.  
  197.                     // rule.endDate is a floating datetime, however per RFC2445
  198.                     // it must be in UTC. Since we want the datepicker to show
  199.                     // the date based on our _local_ timezone, we must first
  200.                     // "pin" the floating datetime to UTC, and then convert
  201.                     // from UTC to our local timezone. We _can't_ simply
  202.                     // convert directly from floating to our local timezone.
  203.                     endDate = endDate.getInTimezone("UTC");
  204.                     endDate = endDate.getInTimezone(calendarDefaultTimezone());
  205.                     setElementValue("recurrence-duration", "until");
  206.                     setElementValue("repeat-until-date", endDate.jsDate);
  207.                 }
  208.             }        
  209.         }
  210.     }
  211. }
  212.  
  213. function saveDialog()
  214. {
  215.     // This works, but if we ever support more complex recurrence,
  216.     // e.g. recurrence for Martians, then we're going to want to
  217.     // not clone and just recreate the recurrenceInfo each time.
  218.     // The reason is that the order of items (rules/dates/datesets)
  219.     // matters, so we can't always just append at the end.  This
  220.     // code here always inserts a rule first, because all our
  221.     // exceptions should come afterward.
  222.     var deckNumber = Number(getElementValue("period-list"));
  223.     
  224.     var recurrenceInfo = null;
  225.     if (window.originalRecurrenceInfo) {
  226.         recurrenceInfo = window.originalRecurrenceInfo.clone();
  227.         var rrules = splitRecurrenceRules(recurrenceInfo);
  228.         if (rrules[0].length > 0)
  229.             recurrenceInfo.deleteRecurrenceItem(rrules[0][0]);
  230.     } else {
  231.         recurrenceInfo = createRecurrenceInfo(window.calendarEvent);
  232.     }
  233.  
  234.     var recRule = createRecurrenceRule();
  235.     switch (deckNumber) {
  236.     case 0:
  237.         recRule.type = "DAILY";
  238.         var ndays = Number(getElementValue("daily-days"));
  239.         recRule.interval = ndays;
  240.         break;
  241.     case 1:
  242.         recRule.type = "WEEKLY";
  243.         recRule.interval = getElementValue("weekly-weeks");
  244.         var onDays = [];
  245.         ["sun", "mon", "tue", "wed", "thu", "fri", "sat"].
  246.             forEach(function(d)
  247.                     {
  248.                         var elem = document.getElementById("weekly-" + d); 
  249.                         if (elem.checked) {
  250.                             onDays.push(elem.getAttribute("value"));
  251.                         }
  252.                     });
  253.         if (onDays.length > 0)
  254.             recRule.setComponent("BYDAY", onDays.length, onDays);
  255.         break;
  256.     case 2:
  257.         recRule.type = "MONTHLY";
  258.         recRule.interval = getElementValue("monthly-months");
  259.         var recurtype = getElementValue("monthly-type");
  260.         switch (recurtype) {
  261.           case "nth-day":
  262.             recRule.setComponent("BYMONTHDAY", 1, [window.startDate.day]);
  263.             break;
  264.           case "nth-week":
  265.             var el = document.getElementById('monthly-nth-week');
  266.             // For more info on where this magic formula comes from, see icalrecur.c,
  267.             // icalrecurrencetype_day_day_of_week()
  268.             recRule.setComponent("BYDAY", 1, [el.week*8 + el.day+1]);
  269.             break;
  270.           case "last-week":
  271.             el = document.getElementById('monthly-last-week');
  272.             recRule.setComponent("BYDAY", 1, [(-1)*(8+Number(el.day)+1)]);
  273.             break;
  274.           case "last-day":
  275.             recRule.setComponent("BYMONTHDAY", 1, [-1]);
  276.             break;
  277.         }
  278.         break;
  279.     case 3:
  280.         recRule.type = "YEARLY";
  281.         var nyears = Number(getElementValue("yearly-years"));
  282.         if (nyears == "")
  283.             nyears = 1;
  284.         recRule.interval = nyears;
  285.         break;
  286.     }
  287.  
  288.     /* figure out how long this event is supposed to last */
  289.     switch(document.getElementById("recurrence-duration").selectedItem.value) {
  290.     case "forever":
  291.         recRule.count = -1;
  292.         break;
  293.     case "ntimes":
  294.         recRule.count = Math.max(1, getElementValue("repeat-ntimes-count"));
  295.         break;
  296.     case "until":
  297.         // get the datetime from the control (which is in localtime),
  298.         // set the time to 23:59:99 and convert that to UTC time.
  299.         var endDate = getElementValue("repeat-until-date")
  300.         endDate.setHours(23);
  301.         endDate.setMinutes(59);
  302.         endDate.setSeconds(59);
  303.         endDate.setMilliseconds(999);
  304.         endDate = jsDateToDateTime(endDate);
  305.         recRule.endDate = endDate;
  306.         break;
  307.     }
  308.  
  309.     recurrenceInfo.insertRecurrenceItemAt(recRule, 0);
  310.  
  311.     return recurrenceInfo;
  312. }
  313.  
  314.  
  315. function updateDeck()
  316. {
  317.     document.getElementById("period-deck").selectedIndex = Number(getElementValue("period-list"));
  318.  
  319.     updateAccept();
  320. }
  321.  
  322. function updateDuration()
  323. {
  324.     var durationSelection = document.getElementById("recurrence-duration").selectedItem.value;
  325.     if (durationSelection == "forever") {
  326.     }
  327.  
  328.     if (durationSelection == "ntimes") {
  329.         setElementValue("repeat-ntimes-count", false, "disabled");
  330.     } else {
  331.         setElementValue("repeat-ntimes-count", "true", "disabled");
  332.     }
  333.  
  334.     if (durationSelection == "until") {
  335.         setElementValue("repeat-until-date", false, "disabled");
  336.     } else {
  337.         setElementValue("repeat-until-date", "true", "disabled");
  338.     }
  339. }
  340.  
  341. function updateAccept()
  342. {
  343.     var acceptButton = document.getElementById("calendar-recurrence-dialog").getButton("accept");
  344.     acceptButton.removeAttribute("disabled", "true");
  345.     document.getElementById("repeat-interval-warning").setAttribute("hidden", true);
  346.     document.getElementById("repeat-numberoftimes-warning").setAttribute("hidden", true);
  347.  
  348.     var interval;
  349.     switch (Number(getElementValue("period-list"))) {
  350.     case 0: // daily
  351.         interval = Number(getElementValue("daily-days"));
  352.         break;
  353.     case 1: // weekly
  354.         interval = Number(getElementValue("weekly-weeks"));
  355.         break;
  356.     case 2: // monthly
  357.         interval = Number(getElementValue("monthly-months"));
  358.         break;
  359.     case 3: // yearly
  360.         interval = Number(getElementValue("yearly-years"));
  361.         break;
  362.     }
  363.     if (interval == "" || interval < 1) {
  364.         document.getElementById("repeat-interval-warning").removeAttribute("hidden");
  365.         acceptButton.setAttribute("disabled", "true");
  366.     }
  367.  
  368.     if (document.getElementById("recurrence-duration").selectedItem.value == "ntimes") {
  369.         var ntimes = getElementValue("repeat-ntimes-count");
  370.         if (ntimes == "" || ntimes < 1) {
  371.             document.getElementById("repeat-numberoftimes-warning").removeAttribute("hidden");
  372.             acceptButton.setAttribute("disabled", "true");
  373.         }
  374.     }
  375.  
  376.     this.sizeToContent();
  377. }
  378.  
  379. function splitRecurrenceRules(recurrenceInfo)
  380. {
  381.     var ritems = recurrenceInfo.getRecurrenceItems({});
  382.  
  383.     var rules = [];
  384.     var exceptions = [];
  385.  
  386.     for each (var r in ritems) {
  387.         if (r.isNegative)
  388.             exceptions.push(r);
  389.         else
  390.             rules.push(r);
  391.     }
  392.  
  393.     return [rules, exceptions];
  394. }
  395.